Single Host Service
One Server all the services
single host service
update system and instal deps
apt -y update && apt -y upgrade && apt -y autoremove && apt -y autoclean
apt -y dist-upgrade
# install base deps
apt install vim apt-transport-https lsb-release ca-certificates wget zstd git
# install pam deps
apt install libpam0g-dev gcc libc6-dev make scdoc
install knot
wget -O /etc/apt/trusted.gpg.d/knot-latest.gpg https://deb.knot-dns.cz/knot-latest/apt.gpg
sh -c 'echo "deb https://deb.knot-dns.cz/knot-latest/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/knot-latest.list'
apt -y update
apt -y install knot knot-dnsutils
mv /etc/knot/knot.conf{,.old}
cp conf/knot.conf /etc/knot/
setup zone in knot
# define the folliwing variables
# DOMAIN="example.com"
# IPV4="127.0.0.1"
# IPV6="::1"
knotc zone-begin ${DOMAIN}.
knotc zone-set -- @ 86400 SOA ${DOMAIN}. root.${DOMAIN}. 2021022500 14400 3600 1209600 3600
knotc zone-set -- @ 86400 NS ${DOMAIN}.
knotc zone-set -- @ 7200 A ${IPV4}
knotc zone-set -- * 7200 A ${IPV4}
knotc zone-set -- @ 7200 AAAA ${IPV6}
knotc zone-set -- * 7200 AAAA ${IPV6}
knotc zone-set -- @ 7200 MX 10 ${DOMAIN}.
knotc zone-set -- * 7200 MX 10 ${DOMAIN}.
knotc zone-set -- @ 7200 TXT "\"v=spf1 mx -all\""
knotc zone-set -- @ 7200 CAA 0 issue "\"letsencrypt.org\""
knotc zone-set -- @ 7200 CAA 0 issue "\"mailto:root@${DOMAIN}\""
knotc zone-set -- _dmarc 7200 TXT "\"v=DMARC1; p=reject; rua=mailto:root@${DOMAIN}; ruf=mailto:root@${DOMAIN};\""
knotc zone-set -- *._domainkey 7200 TXT "\"v=DKIM1; p=\""
knotc zone-set -- _autodiscover._tcp 7200 SRV 0 0 443 autodiscover
knotc zone-set -- _mta-sts 7200 TXT "\"v=STSv1; id=20210228002000Z\""
knotc zone-set -- _smtp._tls 7200 TXT "\"v=TLSRPTv1; rua=mailto:root@${DOMAIN}\""
knotc zone-commit --
install acme.sh
mkdir -p /opt/src
cd /opt/src
git clone https://github.com/acmesh-official/acme.sh.git
cd acme.sh/
./acme.sh --install --home /opt/cert --accountemail "root@${DOMAIN}"
source /opt/cert/acme.sh.env
export KNOT_SERVER="::1"
export KNOT_KEY="hmac-sha384:tsigkey1:example"
acme.sh --issue -d ${DOMAIN} -d *.${DOMAIN} --dns dns_knot
# later you can install the certificate with the follwing command
acme.sh --force --install-cert -d ${DOMAIN} --key-file /etc/maddy/certs/${DOMAIN}/privkey.pem --fullchain-file /etc/maddy/certs/${DOMAIN}/fullchain.pem
install maddy
# install recent golang version
cd /opt/src
wget https://golang.org/dl/go1.16.linux-amd64.tar.gz
tar xf go1.16.linux-amd64.tar.gz
export GOROOT="$PWD/go"
export PATH="$PWD/go/bin:$PATH"
# install maddy
git clone https://github.com/foxcpp/maddy.git
cd maddy/
git checkout v0.4.3
./build.sh
./build.sh install
configure maddy
# dns knot
# set variable
# DOMAIN="example.com"
knotc zone-begin ${DOMAIN}.
knotc zone-set -- default._domainkey 7200 TXT "\"`cat /var/lib/maddy/dkim_keys/*_default.dns`\""
knotc zone-commit --
maddy conf
## Maddy Mail Server - default configuration file (2020-10-11)
# Suitable for small-scale deployments. Uses its own format for local users DB,
# should be managed via maddyctl utility.
# ----------------------------------------------------------------------------
# Base variables
$(hostname) = example.com
$(primary_domain) = $(hostname)
$(local_domains) = $(primary_domain)
tls file /etc/maddy/certs/$(hostname)/fullchain.pem /etc/maddy/certs/$(hostname)/privkey.pem
# ----------------------------------------------------------------------------
auth.pam {
debug no
use_helper no
}
# imapsql module stores all indexes and metadata necessary for IMAP using a
# relational database. It is used by IMAP endpoint for mailbox access and
# also by SMTP & Submission endpoints for delivery of local messages.
storage.imapsql local_mailboxes {
driver sqlite3
dsn imapsql.db
}
# ----------------------------------------------------------------------------
# SMTP endpoints + message routing
hostname $(hostname)
msgpipeline local_routing {
destination postmaster $(local_domains) {
modify {
replace_rcpt file /etc/maddy/aliases
replace_rcpt regexp "(.+)[\.\_\+\-](.+)@(.+)" "$1@$3"
replace_rcpt regexp "(.+)@(.+)\.$(primary_domain)" "$1@$(primary_domain)"
}
deliver_to &local_mailboxes
}
default_destination {
reject 550 5.1.1 "User doesn't exist"
}
}
smtp tcp://0.0.0.0:25 {
limits {
# Up to 20 msgs/sec across max. 10 SMTP connections.
all rate 20 1s
all concurrency 10
}
dmarc yes
check {
require_mx_record
dkim
spf
}
source $(local_domains) {
reject 501 5.1.8 "Use Submission for outgoing SMTP"
}
default_source {
destination postmaster $(local_domains) {
deliver_to &local_routing
}
default_destination {
reject 550 5.1.1 "User doesn't exist"
}
}
}
submission tls://0.0.0.0:465 tcp://0.0.0.0:587 {
limits {
# Up to 50 msgs/sec across any amount of SMTP connections.
all rate 50 1s
}
auth pam
source $(local_domains) {
destination postmaster $(local_domains) {
deliver_to &local_routing
}
default_destination {
modify {
dkim $(primary_domain) $(local_domains) default
}
deliver_to &remote_queue
}
}
default_source {
reject 501 5.1.8 "Non-local sender domain"
}
}
target.remote outbound_delivery {
limits {
# Up to 20 msgs/sec across max. 10 SMTP connections
# for each recipient domain.
destination rate 20 1s
destination concurrency 10
}
mx_auth {
dane
mtasts {
cache fs
fs_dir mtasts_cache/
}
local_policy {
min_tls_level encrypted
min_mx_level none
}
}
}
target.queue remote_queue {
target &outbound_delivery
autogenerated_msg_domain $(primary_domain)
bounce {
destination postmaster $(local_domains) {
deliver_to &local_routing
}
default_destination {
reject 550 5.0.0 "Refusing to send DSNs to non-local addresses"
}
}
}
# ----------------------------------------------------------------------------
# IMAP endpoints
imap tls://0.0.0.0:993 tcp://0.0.0.0:143 {
auth pam
storage &local_mailboxes
}
what mail adresses work with maddy
for the user with the username “test” sending and receiving on:
- user@${DOMAIN}
- test+asd@${DOMAIN}
- test-asd@${DOMAIN}
- test.asd@${DOMAIN}
- test_asd@${DOMAIN} only receiving on:
- test@asd.${DOMAIN}
- test+asd@asd.${DOMAIN}
- test-asd@asd.${DOMAIN}
- test.asd@asd.${DOMAIN}
- test_asd@asd.${DOMAIN}
create system mail addresses
# set variable
# DOMAIN="example.com"
# USERNAME="root"
./aliases.sh ${DOMAIN} ${USERNAME} > /etc/maddy/aliases
aliases.sh
DOMAIN=$1
USER=$2
cat <<_EOF_
info@${DOMAIN}: ${USER}@${DOMAIN}
support@${DOMAIN}: ${USER}@${DOMAIN}
abuse@${DOMAIN}: ${USER}@${DOMAIN}
noc@${DOMAIN}: ${USER}@${DOMAIN}
security@${DOMAIN}: ${USER}@${DOMAIN}
postmaster@${DOMAIN}: ${USER}@${DOMAIN}
hostmaster@${DOMAIN}: ${USER}@${DOMAIN}
webmaster@${DOMAIN}: ${USER}@${DOMAIN}
admin@${DOMAIN}: ${USER}@${DOMAIN}
${DOMAIN}@${DOMAIN}: ${USER}@${DOMAIN}
_EOF_
install and configure nginx
# set variable
# DOMAIN="example.com"
rm -rf /etc/nginx/*
./nginx.conf.sh ${DOMAIN} > /etc/nginx/nginx.conf
nginx.conf.sh
DOMAIN=$1
cat <<_EOF_
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 768;
multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
application/javascript js;
application/atom+xml atom;
application/rss+xml rss;
text/mathml mml;
text/plain txt;
text/vnd.sun.j2me.app-descriptor jad;
text/vnd.wap.wml wml;
text/x-component htc;
image/png png;
image/tiff tif tiff;
image/vnd.wap.wbmp wbmp;
image/x-icon ico;
image/x-jng jng;
image/x-ms-bmp bmp;
image/svg+xml svg svgz;
image/webp webp;
application/font-woff woff;
application/java-archive jar war ear;
application/json json;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
application/postscript ps eps ai;
application/rtf rtf;
application/vnd.apple.mpegurl m3u8;
application/vnd.ms-excel xls;
application/vnd.ms-fontobject eot;
application/vnd.ms-powerpoint ppt;
application/vnd.wap.wmlc wmlc;
application/vnd.google-earth.kml+xml kml;
application/vnd.google-earth.kmz kmz;
application/x-7z-compressed 7z;
application/x-cocoa cco;
application/x-java-archive-diff jardiff;
application/x-java-jnlp-file jnlp;
application/x-makeself run;
application/x-perl pl pm;
application/x-pilot prc pdb;
application/x-rar-compressed rar;
application/x-redhat-package-manager rpm;
application/x-sea sea;
application/x-shockwave-flash swf;
application/x-stuffit sit;
application/x-tcl tcl tk;
application/x-x509-ca-cert der pem crt;
application/x-xpinstall xpi;
application/xhtml+xml xhtml;
application/xspf+xml xspf;
application/zip zip;
application/octet-stream bin exe dll;
application/octet-stream deb;
application/octet-stream dmg;
application/octet-stream iso img;
application/octet-stream msi msp msm;
application/vnd.openxmlformats-officedocument.wordprocessingml.document docx;
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx;
application/vnd.openxmlformats-officedocument.presentationml.presentation pptx;
audio/midi mid midi kar;
audio/mpeg mp3;
audio/ogg ogg;
audio/x-m4a m4a;
audio/x-realaudio ra;
video/3gpp 3gpp 3gp;
video/mp2t ts;
video/mp4 mp4;
video/mpeg mpeg mpg;
video/quicktime mov;
video/webm webm;
video/x-flv flv;
video/x-m4v m4v;
video/x-mng mng;
video/x-ms-asf asx asf;
video/x-ms-wmv wmv;
video/x-msvideo avi;
}
default_type application/octet-stream;
ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
server {
listen 80 default_server;
listen [::]:80 default_server;
listen 443 default_server ssl http2;
listen [::]:443 default_server ssl http2;
ssl_certificate /opt/cert/${DOMAIN}/fullchain.cer;
ssl_certificate_key /opt/cert/${DOMAIN}/${DOMAIN}.key;
root /var/www/html;
index index.html index.htm;
server_name ${DOMAIN};
set \$redirect false;
# enable this to only allow main domain
#if ( \$host != \$server_name) {
# set \$redirect true;
#}
if ( \$scheme = http ) {
set \$redirect true;
}
if ( \$request_uri ~* '/.well-known/acme-challenge/' ) {
set \$redirect false;
}
if ( \$redirect = true) {
return 302 https://\$server_name\$request_uri;
}
location / {
try_files \$uri \$uri/ =404;
}
location ~ ^/~(.+?)(/.*)?$ {
alias /home/\$1/www\$2;
#autoindex on;
}
location ~ /\\.(?!well-known).* {
deny all;
access_log off;
log_not_found off;
}
location = /.well-known/mta-sts.txt {
return 200 "version: STSv1\nmode: enforce\nmax_age: 10368000\nmx: ${DOMAIN}\n";
}
location /autodiscover/autodiscover.xml {
alias /var/www/html/autodiscover.xml;
}
location /.well-known/autoconfig/mail/config-v1.1.xml {
alias /var/www/html/config-v1.1.xml;
}
location /mail/config-v1.1.xml {
alias /var/www/html/config-v1.1.xml;
}
}
}
_EOF_
configure sshd
# uncoment the default sftp Subsystem
# add Deny and Chroot options
cat <<'_EOF_' >> /etc/ssh/sshd_config
#Subsystem sftp /usr/lib/openssh/sftp-server
Subsystem sftp internal-sftp
Port 2222
Match Group sftponly LocalPort 22
DenyGroups sftponly
Match Group sftponly
ChrootDirectory %h
AllowTCPForwarding no
X11Forwarding no
ForceCommand internal-sftp
PasswordAuthentication no
_EOF_
useradd -G sftponly -s /sbin/nologin -m -k ./user-skel/ ${USERNAME}
chown root: /home/${USERNAME}
autodiscover
# set variable
# DOMAIN="example.com"
./autodiscover.xml.sh ${DOMAIN} > /var/www/html/autodiscover.xml
./config-v1.1.xml.sh ${DOMAIN} > /var/www/html/config-v1.1.xml
autodiscover.xml.sh
DOMAIN=$1
cat <<_EOF_
<?xml version="1.0" encoding="UTF-8"?>
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">
<Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
<Account>
<AccountType>email</AccountType>
<Action>settings</Action>
<Protocol>
<Type>IMAP</Type>
<Server>${DOMAIN}</Server>
<Port>993</Port>
<DomainRequired>on</DomainRequired>
<SPA>off</SPA>
<SSL>on</SSL>
<AuthRequired>on</AuthRequired>
</Protocol>
<Protocol>
<Type>SMTP</Type>
<Server>${DOMAIN}</Server>
<Port>465</Port>
<DomainRequired>on</DomainRequired>
<SPA>off</SPA>
<SSL>on</SSL>
<AuthRequired>on</AuthRequired>
<UsePOPAuth>off</UsePOPAuth>
<SMTPLast>off</SMTPLast>
</Protocol>
</Account>
</Response>
</Autodiscover>
_EOF_
config-v1.1.xml.sh
DOMAIN=$1
cat <<_EOF_
<?xml version="1.0"?>
<clientConfig version="1.1">
<emailProvider id="${DOMAIN}">
<domain>${DOMAIN}</domain>
<displayName>${DOMAIN} Mail</displayName>
<displayShortName>${DOMAIN}</displayShortName>
<incomingServer type="imap">
<hostname>${DOMAIN}</hostname>
<port>993</port>
<socketType>SSL</socketType>
<authentication>password-cleartext</authentication>
<username>%EMAILADDRESS%</username>
</incomingServer>
<incomingServer type="imap">
<hostname>${DOMAIN}</hostname>
<port>143</port>
<socketType>STARTTLS</socketType>
<authentication>password-cleartext</authentication>
<username>%EMAILADDRESS%</username>
</incomingServer>
<outgoingServer type="smtp">
<hostname>${DOMAIN}</hostname>
<port>465</port>
<socketType>SSL</socketType>
<authentication>password-cleartext</authentication>
<username>%EMAILADDRESS%</username>
</outgoingServer>
<outgoingServer type="smtp">
<hostname>${DOMAIN}</hostname>
<port>587</port>
<socketType>STARTTLS</socketType>
<authentication>password-cleartext</authentication>
<username>%EMAILADDRESS%</username>
</outgoingServer>
<documentation url="https://${DOMAIN}">
<descr lang="de">Webseite von ${DOMAIN}</descr>
<descr lang="en">Homepage of ${DOMAIN}</descr>
</documentation>
</emailProvider>
</clientConfig>
_EOF_
homepage
# set variable
# DOMAIN="example.com"
./index.htm.sh ${DOMAIN} > /var/www/html/index.html
ToDo
needed:
- autodiscover
- https://www.rzegocki.pl/blog/adding-email-server-autoconfig-and-autodiscover/
- https://web.archive.org/web/20150817115525/http://moens.ch:80/2012/05/31/providing-email-client-autoconfiguration-information/
- https://mcmilk.de/projects/autoconfig/
- https://nginx.tutorials24x7.com/blog/thunderbird-autoconfiguration-using-nginx-server-block
- https://gist.github.com/benhartwich/5ee8dc551554b17cd2fb
- https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration/FileFormat/HowTo
- https://www.heise.de/ct/ausgabe/2013-8-Selbstkonfiguration-von-E-Mail-Clients-2324925.html
- https://blog.bartlweb.net/2015/08/konfigurationsdaten-fur-automatische-einrichtung-von-e-mail-clients-bereitstellen/
- mailclient
- signup
- username restrictions alphanumeric, starting with letter
- signup form username, ssh public key
- management script for changing password
- enable user
- hsts, preload, csp
- expect ct
- x-frame
- xss
- content type
- subdomain wildcard only for receiving
- dnssec
- check with https://www.hardenize.com/
other:
- dane TLSA
- only tls v1.2 and up
- vpn
- mail alias
- cli mail
- pam auth
- per user subdomain